package cn.touna.jss.modules.third.huache;

import cn.touna.risk.api.contraints.facet.First;
import cn.touna.risk.api.contraints.facet.Second;
import cn.touna.risk.api.enums.SystemErrorCodeEnum;
import cn.touna.jss.common.factory.ResponseFactory;
import cn.touna.jss.common.properties.disconf.HuaCheProperties;
import cn.touna.jss.common.util.ValidUtil;
import cn.touna.jss.modules.adapter.ApiMapping;
import cn.touna.jss.modules.third.huache.entity.HuaCheRequestEntity;
import cn.touna.risk.api.domain.Response;
import com.alibaba.fastjson.JSONObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 华策：支付宝、微信、Smart大额分
 */
@Service
public class HuacheService {

    private static final Logger logger = LogManager.getLogger(HuacheService.class);

    @Autowired
    private HuaCheProperties huaCheProperties;

    @Resource(name = "scheduledThredPool")
    private ScheduledExecutorService threadPool;

    private Map<Thread, Response> threadResponseMap = new HashMap<>();


    @ApiMapping(value = "huache.alipay", desc = "华策.支付宝")
    public Response alipay(HuaCheRequestEntity requestEntity) {

        logger.info("REQ 【华策支付宝信息查询请求入参】,params：{}", requestEntity);
        ValidUtil.checkRequestParams(requestEntity, Second.class);

        Map<String, Object> businessParam = new HashMap<String, Object>();
        List<String> mobilePhones = new ArrayList<String>();
        mobilePhones.add(requestEntity.getMobile());
        businessParam.put("mobile_phones", mobilePhones);

        //发起请求
        Response<JSONObject> response = sendAsyncRequest(huaCheProperties.getAlipayAppCode(), businessParam);
        if (response.checkIfFail())
            return response;
        String transactionId = response.getData().getString("transaction_id");
        //获取请求结果
        response = syncRecursiveGetResult(huaCheProperties.getAlipayAppCode(), transactionId);
        response.getData().put("menType", requestEntity.getMenType());
        return response;
    }

    @ApiMapping(value = "huache.weixin", desc = "华策.微信")
    public Response weixin(HuaCheRequestEntity requestEntity) {

        logger.info("REQ 【华策微信信息查询请求入参】,params：{}", requestEntity);
        ValidUtil.checkRequestParams(requestEntity, Second.class);

        Map<String, Object> businessParam = new HashMap<String, Object>();
        List<String> mobilePhones = new ArrayList<String>();
        mobilePhones.add(requestEntity.getMobile());
        businessParam.put("mobile_phones", mobilePhones);
        businessParam.put("name", requestEntity.getName());
        businessParam.put("id_no", requestEntity.getIdCard());

        //发起请求
        Response<JSONObject> response = sendAsyncRequest(huaCheProperties.getWeixinAppCode(), businessParam);
        if (response.checkIfFail())
            return response;

        String transactionId = response.getData().getString("transaction_id");
        //获取请求结果
        response = syncRecursiveGetResult(huaCheProperties.getAlipayAppCode(), transactionId);
        return response;
    }

    @ApiMapping(value = "huache.smartBigAmt", desc = "华策.Smart大额")
    public Response smartBigAmt(HuaCheRequestEntity requestEntity) {

        logger.info("REQ 【华策Smart大额信息查询请求入参】,params：{}", requestEntity);
        ValidUtil.checkRequestParams(requestEntity, First.class);

        Map<String, Object> businessParam = new HashMap<String, Object>();
        List<String> mobilePhones = new ArrayList<String>();
        mobilePhones.add(requestEntity.getMobile());
        businessParam.put("mobile_phones", mobilePhones);

        //发起请求
        Response<JSONObject> response = sendAsyncRequest(huaCheProperties.getSmartBigAmtAppCode(), businessParam);
        if (response.checkIfFail())
            return response;

        String transactionId = response.getData().getString("transaction_id");
        //获取请求结果
        response = syncRecursiveGetResult(huaCheProperties.getAlipayAppCode(), transactionId);
        return response;
    }


    /**
     * 发送异步请求
     *
     * @param appCdoe       接口的ID
     * @param businessParam 请求的参数
     * @return
     */
    private Response<JSONObject> sendAsyncRequest(String appCdoe, Map<String, Object> businessParam) {
        try {
            String result = HuaCheServiceCaller.asyncRequest(huaCheProperties.getBaseUrl(), huaCheProperties.getClientId(),
                    appCdoe, huaCheProperties.getApiVersion(), businessParam,
                    huaCheProperties.getConnectTimeoutMs(), huaCheProperties.getReadTimeoutMs());
            JSONObject jsonObject = JSONObject.parseObject(result);
            JSONObject headerObject = (JSONObject) jsonObject.get("header");
            String status = headerObject.get("status") + "";
            if ("processing".equals(status)) {
                return ResponseFactory.success(headerObject);
            } else
                return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, headerObject.toJSONString());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, e.getMessage());
        }
    }

    /**
     * 获取异步请求的结果
     *
     * @param appCode
     * @param transactionId
     * @return
     */
    private Response asyncRequestResult(String appCode, String transactionId) {
        try {
            String result = HuaCheServiceCaller.getResult(huaCheProperties.getBaseUrl(), huaCheProperties.getClientId(),
                    appCode, huaCheProperties.getApiVersion(), transactionId,
                    huaCheProperties.getConnectTimeoutMs(), huaCheProperties.getReadTimeoutMs());
            JSONObject jsonObject = JSONObject.parseObject(result);
            return ResponseFactory.success(jsonObject);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, e.getMessage());
        }
    }

    /**
     * 同步获取请求的结果
     * @param appCode
     * @param transactionId
     * @return
     */
    private Response syncRecursiveGetResult(String appCode, String transactionId) {
        int count = 0;
        long start = System.currentTimeMillis();
        Response response = asyncRequestResult(appCode, transactionId);
        JSONObject resultJson = (JSONObject) response.getData();
        JSONObject header = (JSONObject) resultJson.get("header");
        String status = header.getString("status");
        if ("processing".equals(status)) {
            // 没有成功那么开始轮询
            while (true) {
                try {
                    count++;
                    if (count >= 20) {
                        break;
                    }
                    response = asyncRequestResult(appCode, transactionId);
                    logger.info("重新请求华策数据,第" + count + "次查询结果:" + response.toString());
                    resultJson = (JSONObject) response.getData();
                    header = (JSONObject) resultJson.get("header");
                    String asynCode = header.getString("status");
                    if ("success".equals(asynCode)) {
                        break;
                    } else if ("failed".equals(asynCode)) {
                        response = ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, header.toJSONString());
                    }
                    TimeUnit.SECONDS.sleep(3);
                } catch (Exception e) {
                    logger.error("华策获取查询结果失败", e);
                    response = ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, e.getMessage());
                    break;
                }
            }
        }
        long cost = System.currentTimeMillis() - start;
        logger.info("华策数据查询消耗时间：" + cost);
        return response;
    }

    /**
     * 定时刷新获取异步请求的结果
     *
     * @param appCode
     * @param transactionId
     * @return
     */
    private Response recursiveGetResult(String appCode, String transactionId) {
        long start = System.currentTimeMillis();
        try {
            Response response = asyncRequestResult(appCode, transactionId);
            JSONObject resultJson = (JSONObject) response.getData();
            JSONObject header = (JSONObject) resultJson.get("header");
            String status = header.getString("status");
            CountDownLatch latch = new CountDownLatch(1);
            if ("processing".equals(status)) {
                HuacheService.RequestResultThread requestResultThread = new HuacheService.RequestResultThread(appCode, transactionId, Thread.currentThread(), threadResponseMap, latch);
                threadPool.execute(requestResultThread);
                try {
                    latch.await();
                    synchronized (threadResponseMap) {
                        response = threadResponseMap.get(Thread.currentThread());
                        threadResponseMap.remove(Thread.currentThread());
                        return response;
                    }
                } catch (Exception e) {
                    logger.error("华策获取查询结果失败", e);
                    return ResponseFactory.fail(SystemErrorCodeEnum.CRS_SYSTEM_FAILED, e.getMessage());
                }
            } else {
                return response;
            }
        } catch (Exception e) {
            logger.error("华策获取查询结果失败", e);
            return ResponseFactory.fail(SystemErrorCodeEnum.JSS_CALL_EXTSYSTEM_ERROR, e.getMessage());
        } finally {
            long cost = System.currentTimeMillis() - start;
            logger.info("华策数据查询消耗时间：" + cost);
        }
    }

    class RequestResultThread implements Runnable {

        private String appCode;
        private String transactionId;
        private Thread rootThread;
        private Map<Thread, Response> map;
        private CountDownLatch latch;
        private int count = 1;

        public RequestResultThread(String appCode, String transactionId, Thread rootThread,
                                   Map<Thread, Response> map, CountDownLatch latch) {
            this.appCode = appCode;
            this.transactionId = transactionId;
            this.rootThread = rootThread;
            this.map = map;
            this.latch = latch;
        }

        @Override
        public void run() {
            Response response;
            try {
                response = asyncRequestResult(appCode, transactionId);
                logger.info("重新请求华策数据，第" + String.valueOf(count++) + "次，thread=" + Thread.currentThread().getName()
                        + "查询结果：" + response.toString());
                JSONObject resultJson = (JSONObject) response.getData();
                JSONObject header = (JSONObject) resultJson.get("header");
                String asynCode = header.getString("status");
                if ("success".equals(asynCode)) {
                    map.put(rootThread, response);
                    latch.countDown();
                } else if ("failed".equals(asynCode)) {
                    response = ResponseFactory.fail(SystemErrorCodeEnum.JSS_EXTSYSTEM_RESP_ERROR, header.toJSONString());
                    map.put(rootThread, response);
                    latch.countDown();
                } else {
                    threadPool.schedule(this, 3, TimeUnit.SECONDS);
                }
            } catch (Exception e) {
                logger.error("重新请求华策数据时异常", e);
                response = ResponseFactory.fail(SystemErrorCodeEnum.CRS_SYSTEM_FAILED, e.getMessage());
                map.put(rootThread, response);
                latch.countDown();
            }
        }
    }

}
